package Master;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;

import javax.swing.JOptionPane;

import Master.MasterEngine.AlgorithmFlag;

/*
 * Several algorithms of scheduling tasks to resources
 */
public class ScheduleAlgorithm {
	private TaskPlan taskPlan = null;
	private ArrayList<Resource> resources = null;
	public static enum CompareFactor {cost, time};
	public static CompareFactor compareFactor = CompareFactor.cost;
    
	public ScheduleAlgorithm(TaskPlan taskPlan){
		this.taskPlan = taskPlan;
		this.resources = taskPlan.getResources();
    }
    
	/*
	 * Interface to schedule based on algorithm flag
	 */
    public boolean basicSchedule(AlgorithmFlag algorithmFlag){
    	
        if(taskPlan==null)
    	{
    		System.out.println("Error: The task plan is null");
    		return true;
    	}
        
        float currentTime, nextCompTime;
    	Resource resource;
		
        for( Task task : taskPlan.getTasks()){
        	if(algorithmFlag == AlgorithmFlag.TIME){
        		compareFactor = CompareFactor.cost;
            	taskPlan.sortResources();
        	}
        	compareFactor = CompareFactor.time;
        	taskPlan.sortResources();
        	
        	currentTime = task.getEstimateTime();
        	resource = taskPlan.getFirstResource();
        	nextCompTime = resource.getNextCompleteTime();
        	nextCompTime += currentTime;
        	
        	if(nextCompTime > taskPlan.getTimeRequired()){
        		return false;
        	}
        	else{
        		resource.addTask(task);
        	}
        } 
        return true;
    }
    
    /*public void fcfsBasedSchedule(TaskPlan taskPlan){

        System.out.println("TaskPlan's tasks size :::::" + taskPlan.getTasks().size());

    	Queue<Task> taskQueue = new LinkedList<Task>();
    	float currentTime, nextCompTime;
    	Resource resource;

        if(taskPlan==null)
    	{
    		System.out.println("Error: The task plan is null");
    		return;
    	}
        
        for(Task task : taskPlan.getTasks()){
            //System.out.println("task ID" + task.getTaskID());
            System.out.println("New Task "+ task.getTaskID());
        	taskQueue.offer(task);

        }

        System.out.println("Task Queue size :::" + taskQueue.size());
		
        for( Task task : taskPlan.getTasks()){
        	compareFactor = CompareFactor.time;
        	taskPlan.sortResources();
        	
        	currentTime = task.getEstimateTime();
        	resource = taskPlan.getFirstResource();
        	nextCompTime = resource.getNextCompleteTime();
        	nextCompTime += currentTime;
        	
        	if(nextCompTime > taskPlan.getTimeRequired()){
        		System.out.println("NOT Feasible!");
        		break;
        	}
        	else{
        		resource.addTask(task);
        	}
        }        
    }*/
    
    /*
     * Cost first
     */
    public boolean costBasedSchedule(){
        // this.taskPlan = taskPlan;
        Queue<Task> taskQueue = new LinkedList<Task>();
        Task firstTask;

        if(taskPlan==null)
    	{
    		System.out.println("Error: The task plan is null");
    		return true;
    	}

        
        for(Task task : taskPlan.getTasks()){
        	taskQueue.offer(task);
        }

        // 1. Sort resources by increasing cost
        taskPlan.sortResources();

        // 2. For each resource in order, assign as many as jobs as possible
        //to the resource without exceeding the deadline
        for(Resource resource : resources){
            if(resource.isAvailable()){
                firstTask = taskQueue.peek();

                while(firstTask != null && 
            		(firstTask.getEstimateTime()+resource.getNextCompleteTime()
            				< taskPlan.getTimeRequired()))
                {
                    firstTask = taskQueue.poll();
                    resource.addTask(firstTask);
                    if(taskQueue.size() != 0){
                        firstTask = taskQueue.peek();
                    } else {
                        break;
                    }

                }
            }
        }
        
        // 3. Repeat all steps until all jobs are proceed
        if( taskQueue.size() != 0) {
            // cost optimize method can not be finished before deadline
    		return false;
        }
        
        return true;
    }
    
    /*
     * FCFS
     */
    public boolean fcfsBasedSchedule(){
    	boolean isFeasible = basicSchedule(AlgorithmFlag.FCFS);
    	if(!isFeasible){
    		showOverDeadlineWarning();
    		return false;
    	}
    	if(isOverBudget()) showOverBudgetWarning();
    	return true;
    }
    
    /*
     * Deadline first
     */
    public boolean timeBasedSchedule(){
    	boolean isFeasible = basicSchedule(AlgorithmFlag.TIME);
    	if(!isFeasible){
    		showOverDeadlineWarning();
    		return false;
    	}
    	if(isOverBudget()) showOverBudgetWarning();
    	return true;
    }
    
    /*
     * Budget and deadline
     */
    public boolean timeBasedBudgetConstrainedSchedule(){
    	boolean isFeasible = basicSchedule(AlgorithmFlag.TIME);
    	if(!isFeasible){
    		showOverDeadlineWarning();
    		return false;
    	}
    	if(!isOverBudget()) return true;
    	
    	/*if(!isOverBudget()){
    		JOptionPane.showMessageDialog(null,"The budget is enough to optimize" +
    				"the time!", "Info", JOptionPane.INFORMATION_MESSAGE);
    		return;
    	}*/
    	
    	JOptionPane.showMessageDialog(null,"The budget constrained is imposed!" +
    			"Hence, The time is not optimized!",
				"Info",JOptionPane.INFORMATION_MESSAGE);
    	clearAllResourceQueues();
    	isFeasible = costBasedSchedule();
    	if(!isFeasible){
    		showOverDeadlineWarning();
    		return false;
    	}
    	if(isOverBudget()) showOverBudgetWarning();
    	return true;
    }
    
    /*
     * Estimate cost of one task on one resource 
     */
    public float getEstimatedCost(){
    	float estimatedCost = 0;
    	for(Resource resource : resources){
    		for(Task task : resource.getTasks()){
    			estimatedCost += resource.getCost() * task.getEstimateTime();
    		}
    	}
    	return estimatedCost;
    }
    
    public boolean isOverBudget(){
    	return (getEstimatedCost() > taskPlan.getBudget()) ;
    }
    
    public void clearAllResourceQueues(){
    	for(Resource resource : resources){
    		resource.clearQueue();
    	}
    }
    
    public void showOverBudgetWarning(){
    	Object[] options = { "OK"};
    	JOptionPane.showOptionDialog(null, "It seems that the given budget "+
    			"is not enough! Penalty may be imposed if you continue", 
    			"Warning",JOptionPane.DEFAULT_OPTION, 
    			JOptionPane.WARNING_MESSAGE, null, options, options[0]);
    }
    
    /*public float getEstimatedTime(){
    	float maxEstimatedTime = 0.0f, curEstimatedTime = 0.0f;
    	for(Resource resource : resources){
    		curEstimatedTime = 0.0f;
    		for(Task task : resource.getTasks()){
    			curEstimatedTime += task.getEstimateTime();
    		}
    		if (maxEstimatedTime < curEstimatedTime)
    			maxEstimatedTime = curEstimatedTime;
    	}
    	return maxEstimatedTime;
    }
    
    public boolean isOverDeadLine(){
    	return (getEstimatedTime() > taskPlan.getTimeRequired());
    }*/
    
    public void showOverDeadlineWarning(){
    	JOptionPane.showMessageDialog(null,"Infeasible! The task plan is "+
    			"unlikey to be completed before deadline! This task plan is "+
    			"cancelled!","Info",JOptionPane.INFORMATION_MESSAGE);
    }
}
